package com.jec.module.sysmanage.service;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedDeque;

import javax.annotation.PostConstruct;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.jec.base.entity.PageList;
import com.jec.module.sysmanage.dao.SysLogRepository;
import com.jec.module.sysmanage.dao.SysLogViewRepository;
import com.jec.module.sysmanage.dao.UserResourceRepository;
import com.jec.module.sysmanage.entity.OperateLog;
import com.jec.module.sysmanage.entity.UserResource;
import com.jec.module.sysmanage.entity.view.OperateLogView;

/**
 * Created by jeremyliu on 7/24/16.
 */
@Service
public class SysLogService extends Thread{
    
    @Autowired
    private SysLogViewRepository sysLogViewRepository;

    @Autowired
    private UserResourceRepository userResourceRepository;
    
    @Autowired
    private SysLogRepository sysLogRepository;

    private Queue<OperateLog> queue = new ConcurrentLinkedDeque<>();

    private Map<String, String> resourceMap = new HashMap<>();

    private final static int batch = 10;

    @PostConstruct
    public void init(){
    	
        List<UserResource> userResources = userResourceRepository.getByLog(true);
        for(UserResource userResource: userResources)
            for(String url: userResource.getUrlList())
                if(!url.equals(""))
                    resourceMap.put(url.trim(), userResource.getId());
    	
        this.start();
       
    }

    public void addLog(int userId, String resourceId, String desc) {

        OperateLog operateLog = new OperateLog();
        operateLog.setOperater(userId);
        operateLog.setResourceId(resourceId);
        operateLog.setDescription(desc);
        operateLog.setCreateTime(new Date());
        
        //saveLogs(operateLog);
        queue.add(operateLog);
        //sysLogDao.save(operateLog);
    }

    public void addLog(String resourceId, String desc){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        HttpSession session = request.getSession();
        Integer userId = (Integer) session.getAttribute("userId");
        if(userId != null)
            addLog(userId, resourceId, desc);
    }

    public void addLog(String desc){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        String uri = StringUtils.trimLeadingCharacter(request.getPathInfo(),'/');
        String resId = resourceMap.get(uri);
        if(resId!=null)
            addLog(resId, desc);
    }

    @Transactional(readOnly = true)
    public PageList<OperateLogView> fetchLog(int start, int page,
                                             Date startDate, Date endDate,
                                             final String searchKey) {
    	
    	 PageRequest pageRequest =  new PageRequest(start/page, page); 
    	
    	 Specification<OperateLogView> spec = new Specification<OperateLogView>(){  
            @Override  
            public Predicate toPredicate(Root<OperateLogView> root,  
                    CriteriaQuery<?> query, CriteriaBuilder builder) { 
            	
            	List<Predicate> predicates = new ArrayList<>();
            	
            	if(startDate != null) {
            		predicates.add(
            				builder.greaterThanOrEqualTo(root.get("createTime").as(Date.class), startDate)
            				);
            	}
            	
            	if(endDate != null) {
            		predicates.add(
            				builder.lessThanOrEqualTo(root.get("createTime").as(Date.class), endDate)
            				);
            	}
            	
            	if(!StringUtils.isEmpty(searchKey)) {
            		
            		List<Predicate> predicatesOr = new ArrayList<>();
            		
            		predicatesOr.add(
            				builder.like(root.get("operator").as(String.class), "%" + searchKey + "%")
            				); 
            		
            		predicatesOr.add(
            				builder.like(root.get("module").as(String.class), "%" + searchKey + "%")
            				); 
            		
            		predicatesOr.add(
            				builder.like(root.get("description").as(String.class), "%" + searchKey + "%")
            				); 
            		
            		predicates.add(
            				builder.or(predicatesOr.toArray(new Predicate[predicates.size()]))
            				);
            	}
 
                return builder.and(predicates.toArray(new Predicate[predicates.size()]));  
            }  
        };
    	
        Page<OperateLogView> found = sysLogViewRepository.findAll(spec, pageRequest);
        
        return new PageList<>(
        		found.getContent(), 
        		found.getTotalElements(), 
        		found.getTotalPages(), 
        		(start+page)/page);
        
    }
    
    @Transactional
    public void batchRemoveLog(Date startDate, Date endDate, String searchKey) {
    	
    	PageList<OperateLogView> pl = fetchLog(0, Integer.MAX_VALUE, startDate, endDate, searchKey);
    	
    	for(OperateLogView v : pl.getData())
    		sysLogRepository.delete(v.getId());
    	
    }
    
    @Transactional
    public void batchRemoveLog(Integer[] ids){
    	if(ids != null) {
    		for(Integer id : ids) this.sysLogRepository.delete(id);
    	}
        //sysLogDao.removeByIds((Serializable[])ids);
    }
    
    @Transactional
    public void batchRemoveAll(){
    	List<OperateLog> found = sysLogRepository.findAll();
    	sysLogRepository.delete(found);
    }
    
    @Transactional
    public void saveLogs(OperateLog ...operateLogs){
    	sysLogRepository.save(Arrays.asList(operateLogs));
    }

    @Override
    public void run(){
        TransactionSynchronizationManager.initSynchronization();
        //OperateLog[] operateLogs = new OperateLog[batch];
        ArrayList<OperateLog> operateLogs = new ArrayList<OperateLog>(batch);
        while(true){

            for(int i=0; i<batch; i++){
                if(queue.size() == 0) {
                	/*
                    if(i > 0)
                    	this.saveLogs(Arrays.copyOfRange(operateLogs, 0, i));
                        //sysLogDao.saveForce(Arrays.copyOfRange(operateLogs, 0, i));
                    */
                	if(!operateLogs.isEmpty()) {
                		sysLogRepository.save(operateLogs);
                		operateLogs.clear();
                	}
                    break;
                }else {
                    OperateLog operateLog = queue.poll();
                    //operateLogs[i] = operateLog;
                    operateLogs.add(operateLog);
                }
            }
            
            try {
                Thread.sleep(50);
            }catch (Exception e){
                e.printStackTrace();
            }
            
        }
        
    }
    

}


